package com.test.service;

import cn.hutool.json.JSONUtil;
import co.nstant.in.cbor.CborBuilder;
import co.nstant.in.cbor.CborEncoder;
import co.nstant.in.cbor.model.ByteString;
import co.nstant.in.cbor.model.DataItem;
import co.nstant.in.cbor.model.UnsignedInteger;
import com.alibaba.fastjson.JSONObject;
import com.test.bean.CommonRequest;
import com.test.bean.ListObjectRequest;
import com.test.bean.RpcRequest;
import com.test.bean.result.ChainHeadResult;
import com.test.bean.result.CommonResult;
import com.test.bean.result.LotusChainResult;
import com.test.constant.FilConstants;
import com.test.constant.FilecoinCnt;
import com.test.exception.AddressException;
import com.test.forest.RpcClient;
import com.test.utils.AddressUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

@Component
@Slf4j
public class FileCoinService {
    @Autowired
    RpcClient rpcClient;

    @Value("${lotus.address}")
    private String address;


    public CommonResult newAuth() {
        CommonRequest request = new CommonRequest();
        request.setId(7878);
        request.setParams(new String[]{});
        request.setMethod(FilConstants.AUTH_NEW);
        String jsonStr = "{\"jsonrpc\":\"2.0\",\"method\":\"Filecoin.AuthNew\",\"params\":[[\"admin\"]],\"id\":7878}";
        log.info("jsonStr:{}", jsonStr);
        CommonResult result = rpcClient.newAuth(jsonStr);
        return result;
    }

    /**
     * 获得钱包余额
     *
     * @return
     */
    public BigDecimal getWalletBalance() {
        CommonRequest request = new CommonRequest();
        request.setId(1);
        request.setMethod(FilConstants.WALLET_BALANCE);
        String[] params = {address};
        request.setParams(params);
        String result = rpcClient.walletBalance(request);
        if (result != null) {
            JSONObject jsonBody = JSONObject.parseObject(result);
            if (jsonBody != null) {
                String balance = jsonBody.getString("result");
                if (balance != null) {
                    return (new BigDecimal(balance)).divide(FilConstants.FIL_UNIT, 8, RoundingMode.HALF_DOWN);
                }
            }
        }
        return BigDecimal.ZERO;
    }

    /**
     * 获取当前链头
     *
     * @return
     */
    public LotusChainResult getChainHead() {
        RpcRequest request = new RpcRequest();
        request.setId(1);
        request.setMethod(FilConstants.GET_CHAIN_HEAD);
        request.setParams(new ArrayList<>());
        return rpcClient.getChainHead(request);
    }

    /**
     * 获取随机数
     *
     * @return
     */
    public BigInteger getNonce() {
        CommonRequest request = new CommonRequest();
        request.setId(1);
        request.setMethod(FilConstants.GET_NONCE);
        String[] params = {address};
        request.setParams(params);
        String body = rpcClient.getNonce(request);
        if (body != null) {
            JSONObject jsonBody = JSONObject.parseObject(body);
            if (jsonBody != null) {
                String balance = jsonBody.getString("result");
                if (balance != null) {
                    System.out.println("nonce=" + new BigDecimal(balance).toBigInteger());
                    return new BigDecimal(balance).toBigInteger();
                }
            }
        }
        return BigInteger.ZERO;
    }


    public String getGasEstimateFeeCap(BigInteger nonce, String value) {
        String from = address;
        String to = address;
        JSONObject cid = new JSONObject();
        JSONObject message = new JSONObject();
        cid.put("/", "");
        message.put("To", to);
        message.put("From", from);
        message.put("Nonce", nonce);
        message.put("GasLimit", 0);
        message.put("Value", value);
        message.put("GasFeeCap", "0");
        message.put("GasPremium", "0");
        message.put("Method", 0);
        message.put("Params", "");
        message.put("CID", cid);
        return message.toJSONString();
    }

    /**
     * 获得预估的gas费封顶值
     *
     * @param message
     * @return
     */
    public String getGasEstimateFeeCap(String message) {
        JSONObject jsonObject = JSONObject.parseObject(message);
        List<JSONObject> jsonObjects = new ArrayList<>();
        jsonObjects.add(jsonObject);
        jsonObjects.add(null);
        jsonObjects.add(null);
        RpcRequest request = new RpcRequest();
        request.setParams(jsonObjects);
        request.setId(1);
        request.setMethod(FilConstants.GET_ESTIMATE_GAS_FEE_CAP);
        String body = rpcClient.getGasEstimateFeeCap(request);
        if (body != null) {
            JSONObject jsonBody = JSONObject.parseObject(body);
            if (jsonBody != null) {
                String result = jsonBody.getString("result");
                if (result != null) {
                    return result;
                }
            }
        }
        return "0";
    }

    /**
     * 校验地址
     *
     * @return
     */
    public CommonResult checkAddress() {
        CommonRequest request = new CommonRequest();
        request.setId(1);
        request.setMethod(FilConstants.WALLET_VALIDATE_ADDRESS);
        String[] params = {address};
        request.setParams(params);
        CommonResult result = rpcClient.checkAddress(request);
        return result;
    }


    /**
     * 获得当前区块的cid
     *
     * @return
     */
    public String getBlockCid() {
        LotusChainResult chainHead = getChainHead();
        ChainHeadResult result = chainHead.getResult();
        String cid = result.getCids().get(0).get("/");
        return cid;
    }

    /**
     * 估算交易需要消耗的gas
     *
     * @return
     */
    public String getGasEstimateGasLimit(String gaslimit, String cid) {
        JSONObject jsonlimit = JSONObject.parseObject(gaslimit);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("/", cid);
        List<JSONObject> jsonObjects = new ArrayList<>();
        jsonObjects.add(jsonObject);
        List<Object> info = new ArrayList<>();
        info.add(jsonlimit);
        info.add(jsonObjects);
        ListObjectRequest objectRequest = new ListObjectRequest();
        objectRequest.setParams(info);
        objectRequest.setMethod(FilConstants.GET_ESTIMATE_GAS_FEE_LIMIT);
        objectRequest.setId(1);
        String gasEstimateGasLimit = rpcClient.getGasEstimateGasLimit(objectRequest);
        return gasEstimateGasLimit;
    }

    public String walletSign() {
        String[] params = {address};
        CommonRequest request = new CommonRequest();
        request.setId(1);
        request.setMethod(FilConstants.WALLET_SIGN);
        request.setParams(params);
        CommonResult result = rpcClient.walletSign(request);
        return result.toString();
    }

    public String mpoolPushMessage() throws AddressException {
        int versions = 0;
//        ByteString fromByte = new ByteString(AddressUtil.initAddress(address).getBytes());
//
//        ByteString toByte = new ByteString(AddressUtil.initAddress(address).getBytes());
//
//        UnsignedInteger versionByte = new UnsignedInteger(versions);
//
//        UnsignedInteger nonceByte = new UnsignedInteger(0);
//
//        byte[] valueBytes = new BigInteger(String.valueOf(0)).toByteArray();
//        valueBytes = WriteMajorTypeHeaderBuf(valueBytes, FilecoinCnt.MajUnsignedInt, String.valueOf(0));
//        ByteString valueByte = new ByteString(valueBytes);
//
//        byte[] gasFeeCapBytes = new BigInteger(String.valueOf(0)).toByteArray();
//        gasFeeCapBytes = WriteMajorTypeHeaderBuf(gasFeeCapBytes, FilecoinCnt.MajUnsignedInt, String.valueOf(0));
//        ByteString gasFeeCapByte = new ByteString(gasFeeCapBytes);
//
//        byte[] gasPeremiumBytes = new BigInteger(String.valueOf(0)).toByteArray();
//        gasPeremiumBytes = WriteMajorTypeHeaderBuf(gasPeremiumBytes, FilecoinCnt.MajUnsignedInt, String.valueOf(0));
//        ByteString gasPeremiumByte = new ByteString(gasPeremiumBytes);
//
//        UnsignedInteger gasLimitByte = new UnsignedInteger(0);
//
//        UnsignedInteger methodByte = new UnsignedInteger(1);
//
//        ByteString paramsByte = new ByteString(new byte[]{});
//        List<DataItem> build = new CborBuilder().addArray()
//                .add(versionByte)
//                .add(toByte)
//                .add(fromByte)
//                .add(nonceByte)
//                .add(valueByte)
//                .add(gasLimitByte)
//                .add(gasFeeCapByte)
//                .add(gasPeremiumByte)
//                .add(methodByte)
//                .add(paramsByte)
//                .end().build();
        JSONObject message = new JSONObject();
        message.put("To", address);
        message.put("From", address);
        message.put("GasLimit", 0);
        message.put("Value", "0");
        message.put("GasFeeCap", "0");
        message.put("GasPremium", "0");
        message.put("Method", 0);
        message.put("Version", 0);
        message.put("Params", "ZXlKaGJHY2lPaUpJVXpJMU5pSXNJblI1Y0NJNklrcFhWQ0o5LmV5SkJiR3h2ZHlJNld5SmhaRzFwYmlKZGZRLmJSQXpDanlhak9pQ3V1Y3ZzZldkU01DSVk5ek1TT2trZjdyaTFGTU9teGs");
        String s = message.toJSONString();
        JSONObject jsonObject = JSONObject.parseObject(s);
        List<JSONObject> jsonObjects = new ArrayList<>();
        jsonObjects.add(jsonObject);
        jsonObjects.add(null);
        RpcRequest request = new RpcRequest();
        request.setParams(jsonObjects);
        request.setId(1);
        request.setMethod(FilConstants.MPOOL_PUSH_MESSAGE);
        String str = JSONUtil.toJsonStr(request);
        log.info("str:{}", str);
//        CommonResult result = rpcClient.mpoolPushMessage(request);
        return "result.toString()";
    }

    private byte[] WriteMajorTypeHeaderBuf(byte[] bytes, int c, String value) {
        if (bytes[0] != 0) {
            byte[] buf = new byte[bytes.length + 1];
            buf[0] = (byte) c;
            System.arraycopy(bytes, 0, buf, 1, bytes.length);
            return buf;
        }
        return bytes;
    }
}
